#include "model/BankSession.h"
#include "model/BankSystemConfig.h"
#include "common/Singleton.h"

#include <muduo/base/Atomic.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Thread.h>
#include <muduo/base/ThreadPool.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/TcpServer.h>
#include <muduo/base/ThreadLocalSingleton.h>

#include <boost/bind.hpp>
#include <boost/function.hpp>

#include <stdio.h>
#include <unistd.h>
#include <vector>

using namespace muduo;
using namespace muduo::net;

using namespace PUBLIC;

using namespace std;

class BankServer
{
	TcpServer server_;
	ThreadPool threadPool_;
	int numThreads_;
	Timestamp startTime_;
	typedef	ThreadLocalSingleton<BankSession> LocalBankSession;
public:
	BankServer(EventLoop *loop,const InetAddress &listenAddr,int numThreads)
		: server_(loop,listenAddr,"ABC_BankServer"),
			numThreads_(numThreads),startTime_(Timestamp::now())
	{
		server_.setConnectionCallback(boost::bind(&BankServer::onConnection,this,_1));
		server_.setMessageCallback(boost::bind(&BankServer::onMessage,this,_1,_2,_3));
		//server_.setWriteCompleteCallback(boost::bind(&BankServer::onWriteComplete,this,_1));
	}
	void start()
	{
		LOG_INFO << " starting " << numThreads_ << " threads.";
		threadPool_.setThreadInitCallback(boost::bind(&threadInit));
		threadPool_.start(numThreads_);
		server_.start();//先启动线程池，是怕IO线程启动后接收到请求线程池还没有启动好。
	}
private:
	void onConnection(const TcpConnectionPtr &conn)
	{
		LOG_TRACE << conn->peerAddress().toIpPort() << " -> "
			<< conn->localAddress().toIpPort() << " is "
			<< (conn->connected() ? "UP" : "DOWN");
		if(conn->connected()) {
			conn->setTcpNoDelay(true);
		}
	}
	void onMessage(const TcpConnectionPtr &conn,Buffer *buf,Timestamp)
	{
		//解包送入到计算线程中处理，最好是注册一个回调到该类中
		//一条龙处理完成后发送
		while(buf->readableBytes() >= sizeof(RequestHead)) {
			const void *data = buf->peek();
			const RequestHead *prh = static_cast<const RequestHead *>(data);
			uint16_t cmd = muduo::net::sockets::networkToHost16(prh->cmd);
			uint16_t len = muduo::net::sockets::networkToHost16(prh->len);
			//LOG_INFO << "cmd : " << cmd << " len : " << len;
			if(len > 65536 || len < 0) {
				LOG_ERROR << "Invalid length " << len;
				conn->forceCloseWithDelay(1);
				break;
			} else {
				//接收到数据后调用相关函数进行处理
				//sizeof(RequestHead) + len
				vector<uint8_t> v;
				uint32_t procLen = sizeof(RequestHead) + len;
				if(buf->readableBytes() < procLen) {
					LOG_ERROR << "Invalid package lenght " << procLen;
					conn->forceCloseWithDelay(1);
					break;
				}
				v.resize(procLen);
				memcpy(&v[0],data,procLen);
				threadPool_.run(boost::bind(&processBankSession,conn,v));
				buf->retrieve(procLen);
			}
		}
	}
	#if 0
	void onWriteComplete(const TcpConnectionPtr &conn)
	{
		//关于那三个query的事情，通过tcpdump抓包发现是OK的，
		//但是客户端没有接收到。到底是什么原因。
		LOG_INFO << "send complete!";
	}
	#endif
	static void threadInit()
	{
		//运行在线程中
		assert(LocalBankSession::pointer() == NULL);
		LocalBankSession::instance();
		assert(LocalBankSession::pointer() != NULL);
	}
	static void bankSessionSendCallback(const TcpConnectionPtr &conn,
										const char *buf,size_t len)
	{
		//运行在线程中
		conn->send(buf,len);
	}
	static void processBankSession(const TcpConnectionPtr &conn,
									const vector<uint8_t> &buf)
	{
		////运行在线程中
		//内部一个是需要设置发送回调，一个是需要传入缓冲区数据
		LocalBankSession::instance().setBankSessionSendCallback
					(boost::bind(&bankSessionSendCallback,conn,_1,_2));
		LocalBankSession::instance().Process((const char *)&buf[0],buf.size());
	}
};

int main(int argc,char *argv[])
{
	BankSystemConfig& sysconf = Singleton<BankSystemConfig>::Instance();
	EventLoop loop;
	InetAddress listenAddr(sysconf.GetPort());
	BankServer server(&loop,listenAddr,4);
	server.start();
	loop.loop();
}
